/**
 *   Copyright 2014 Royce Remulla
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package cjcast.common;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ph.rye.common.lang.ObjectUtil;

/**
 * Base implementation for solution. <br/>
 * Follows the format: <br/>
 * <ul>
 * <li>First line is the number of cases.
 * <li>Subsequent lines are the actual cases.
 * </ul>
 * 
 * <pre>
 * $Author: royce.com@gmail.com $ 
 * $Date: 2015-02-18 05:52:25 +0000 (Wed, 18 Feb 2015) $
 * </pre>
 * 
 * 
 * @param <T> Case bean type.
 * @author r39
 */
public abstract class AbstractSolution<T> implements Solution<T> {


    /** */
    private static final Logger LOGGER = LogManager
        .getLogger(AbstractSolution.class);


    /** File reader/writer. */
    private final transient ReadWriteTextFileJDK7 fileUtil = new ReadWriteTextFileJDK7();

    /** */
    private final transient ObjectUtil objUtil = new ObjectUtil();


    /** Number of input lines per case. */
    protected abstract int getCaseUnitSize();

    /** Parse case input line(s). */
    protected abstract T parseInputLines(String... inputLineArr);


    /** {@inheritDoc} */
    @Override
    public CjInputReader<T> getNewInput(final File inputFile)
            throws IOException
    {
        return new CjInputReader<T>(
            inputFile,
            getCaseUnitSize(),
            new CaseConverter<T>() {

                @Override
                public T convert(final String... string)
                {
                    return parseInputLines(string);
                }
            });
    }

    /** {@inheritDoc} */
    @Override
    public void execute(final String pOutputPath)
    {

        assert pOutputPath != null : "Output path must not be null.";

        final long start = System.currentTimeMillis();

        String outputPath;
        if (pOutputPath.endsWith(File.separator)) {
            outputPath = pOutputPath;
        } else {
            outputPath = pOutputPath + File.separator;
        }

        final String clsName = getClass().getName().replace('.', '/');

        final URL urlSmall = getClass()
            .getResource('/' + clsName + ".small.in");
        final File fileSmall = new File(urlSmall.getPath());
        final URL urlLarge = getClass()
            .getResource('/' + clsName + ".large.in");
        final File fileLarge = new File(urlLarge.getPath());

        try {
            generateOutput(fileSmall, new File(outputPath
                    + getClass().getSimpleName() + ".small.out"));
            generateOutput(fileLarge, new File(outputPath
                    + getClass().getSimpleName() + ".large.out"));

        } catch (final IOException e) {
            LOGGER.error(e.getMessage(), e);
        }

        LOGGER.info(System.currentTimeMillis() - start + " ms.");

    }

    /**
     * This will generate the output file for submission to google.
     * 
     * @param inputFile input file.
     * @param outpuFile the result file path.
     * @throws IOException if error occurs while parsing the file.
     */
    public void generateOutput(final File inputFile, final File outpuFile)
            throws IOException
    {
        final CjInputReader<T> inputReader = getNewInput(inputFile);
        final List<String> output = new ArrayList<String>();

        for (final T caseBean : inputReader) {
            if (getClass().isAnnotationPresent(Isolated.class)) {
                final Isolated isolated = getClass().getAnnotation(
                    Isolated.class);
                if (isolated.caseIndex() == inputReader.getCaseCurrentIndex()) {

                    final String result = solve(caseBean);
                    output.add("Case #" + inputReader.getCaseCurrentIndex()
                            + ": " + result);
                }
            } else {
                final String result = solve(caseBean);
                output.add("Case #" + inputReader.getCaseCurrentIndex() + ": "
                        + result);
            }


        }
        LOGGER.info(output);

        getFileUtil().writeLargeTextFile(outpuFile.getAbsolutePath(), output);
        LOGGER.info("Output written to: " + outpuFile.getAbsolutePath());

    }


    public ObjectUtil getObjUtil()
    {
        return this.objUtil;
    }

    /**
     * @return the fileUtil
     */
    public ReadWriteTextFileJDK7 getFileUtil()
    {
        return this.fileUtil;
    }

}